package com.xiaoshuidi.cloud.module.finance.service.order.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageParam;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.mybatis.core.query.QueryWrapperX;
import com.xiaoshuidi.cloud.framework.pay.config.PayProperties;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayClient;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayClientFactory;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayCommonResult;
import com.xiaoshuidi.cloud.framework.pay.core.client.dto.*;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.bean.DivideRule;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.MemberBindReqDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.MemberBindRespDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.MemberCreateReqDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.MemberCreateRespDTO;
import com.xiaoshuidi.cloud.framework.pay.core.enums.PayChannelEnum;
import com.xiaoshuidi.cloud.framework.pay.core.enums.PayStatusEnum;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.module.contract.api.ContractApi;
import com.xiaoshuidi.cloud.module.contract.api.ContractRentOrderApi;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderPayStatusEnum;
import com.xiaoshuidi.cloud.module.contract.vo.ContractRoomReqVO;
import com.xiaoshuidi.cloud.module.contract.vo.OrderFeeAmountReqVO;
import com.xiaoshuidi.cloud.module.contract.vo.OrderFeeAmountRespVO;
import com.xiaoshuidi.cloud.module.contract.vo.RentOrderExtVO;
import com.xiaoshuidi.cloud.module.finance.api.finance.FinancePaymentApi;
import com.xiaoshuidi.cloud.module.finance.api.finance.dto.FinancePaymentDTO;
import com.xiaoshuidi.cloud.module.finance.api.order.dto.PayOrderCreateReqDTO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.collection.vo.FinanceCollectionConfigRespVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.divide.FinanceDivideRuleRespVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.order.vo.PayOrderExportReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.order.vo.PayOrderPageReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.app.order.vo.AppPayOrderSubmitReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.app.order.vo.AppPayOrderSubmitRespVO;
import com.xiaoshuidi.cloud.module.finance.convert.order.PayOrderConvert;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceAccountDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceAccountJournalDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceDivideAccountDo;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceDivideDetailDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceMoneyFlowDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.merchant.PayAppDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.merchant.PayChannelDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.order.PayOrderDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.order.PayOrderExtensionDO;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceAccountJournalMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceAccountMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceDivideDetailMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceMoneyFlowMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.notify.order.PayOrderExtensionMapper;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.notify.order.PayOrderMapper;
import com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants;
import com.xiaoshuidi.cloud.module.finance.enums.finance.DirectionEnum;
import com.xiaoshuidi.cloud.module.finance.enums.finance.DivideModeEnum;
import com.xiaoshuidi.cloud.module.finance.enums.finance.PayChannelTypeEnum;
import com.xiaoshuidi.cloud.module.finance.enums.finance.PaymentSourceEnum;
import com.xiaoshuidi.cloud.module.finance.enums.finance.SourceBizTypeEnum;
import com.xiaoshuidi.cloud.module.finance.enums.order.FinanceCommonStatusEnum;
import com.xiaoshuidi.cloud.module.finance.enums.order.PayOrderNotifyStatusEnum;
import com.xiaoshuidi.cloud.module.finance.enums.refund.PayRefundTypeEnum;
import com.xiaoshuidi.cloud.module.finance.framework.web.config.PayWhitelist;
import com.xiaoshuidi.cloud.module.finance.mq.producer.CouponUnlockDelayProducer;
import com.xiaoshuidi.cloud.module.finance.mq.producer.PayResultProducer;
import com.xiaoshuidi.cloud.module.finance.service.collection.FinanceCollectionConfigService;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceMoneyFlowService;
import com.xiaoshuidi.cloud.module.finance.service.finance.impl.FinanceDivideitory;
import com.xiaoshuidi.cloud.module.finance.service.merchant.PayAppService;
import com.xiaoshuidi.cloud.module.finance.service.merchant.PayChannelService;
import com.xiaoshuidi.cloud.module.finance.service.order.PayOrderService;
import com.xiaoshuidi.cloud.module.member.api.coupon.CouponApi;
import com.xiaoshuidi.cloud.module.member.api.coupon.vo.CouponLockVo;
import com.xiaoshuidi.cloud.module.member.api.coupon.vo.CouponUnLockVo;
import com.xiaoshuidi.cloud.module.member.api.coupon.vo.CouponUpdateReturn;
import com.xiaoshuidi.cloud.module.member.api.coupon.vo.CouponUpdateVo;
import com.xiaoshuidi.cloud.module.member.api.user.MemberUserApi;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserRespDTO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.ThirdPayUserReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Arrays;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils.toJsonString;
import static com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants.*;

/**
 * 支付订单 Service 实现类
 *
 * @author aquan
 */
@Service
@Validated
@Slf4j
public class PayOrderServiceImpl implements PayOrderService {
    @Resource
    private PayProperties payProperties;
    @Resource
    private PayClientFactory payClientFactory;
    @Resource
    private PayOrderMapper orderMapper;

    @Resource
    private FinanceDivideDetailMapper financeDivideDetailMapper;
    @Resource
    private PayOrderExtensionMapper orderExtensionMapper;
    @Resource
    private PayAppService appService;
    @Resource
    private PayChannelService channelService;
    @Resource
    private FinanceDivideitory financeDivideService;

    @Resource
    private FinanceCollectionConfigService financeCollectionConfigService;
    @Resource
    private PayResultProducer payResultProducer;
    @Resource
    private FinanceMoneyFlowMapper financeMoneyFlowMapper;
    @Resource
    private FinanceMoneyFlowService financeMoneyFlowService;
    @Resource
    private FinanceAccountJournalMapper financeAccountJournalMapper;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private CouponApi couponApi;

    @Resource
    private MemberUserApi memberUserApi;

    @Resource
    private CouponUnlockDelayProducer couponUnlockDelayProducer;

    @Resource
    private ContractRentOrderApi contractRentOrderApi;
    @Value("${spring.profiles.active}")
    private String profile;
    @Resource
    @Qualifier("financePaymentApiMap")
    private Map<String, FinancePaymentApi> financePaymentApiObjectProvider;
    @Resource
    private PayWhitelist payWhitelist;
    private Integer expireMinutes = 5;

    @Resource
    private ContractApi contractApi;
    @Resource
    private FinanceAccountMapper financeAccountMapper;

    @Override
    public PayOrderDO getOrder(Long id) {
        return orderMapper.selectById(id);
    }

    @Override
    public PageResult<PayOrderDO> getOrderPage(PayOrderPageReqVO pageReqVO) {
        return orderMapper.selectPage(pageReqVO);
    }

    @Override
    public List<PayOrderDO> getOrderList(PayOrderExportReqVO exportReqVO) {
        return orderMapper.selectList(exportReqVO);
    }

    @Override
    public List<PayOrderDO> getOrderSubjectList(Collection<Long> idList) {
        return orderMapper.findByIdListQueryOrderSubject(idList);
    }

    /**
     * 创建交易流水(线下场景)
     *
     * @param reqDTO 创建请求
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPayFlow(PayOrderCreateReqDTO reqDTO) {
        CommonResult<RentOrderExtVO> rentOrder = null;
        //租金账单如果已经支付 禁止支付
        if (SourceBizTypeEnum.RENT_ORDER.name().equals(reqDTO.getSourceBizType())) {
            rentOrder = contractRentOrderApi.getById(Long.parseLong(reqDTO.getSourceBizId()));
            if (rentOrder.isSuccess() && ObjectUtil.isNotEmpty(rentOrder.getData())) {
                if (!CollectionUtil.newArrayList(OrderPayStatusEnum.PART.getCode(), OrderPayStatusEnum.UNPAID.getCode())
                        .contains(rentOrder.getData().getPayStatus())) {
                    throw new ServiceException(ErrorCodeConstants.FINANCE_MONEY_FLOW_REPEATED_PAYMENTS);
                }
                if(reqDTO.getPrice().subtract(rentOrder.getData().getTotalAmount().subtract(rentOrder.getData().getPaidRentUtilities())).doubleValue() > 0){
                    throw new ServiceException(ErrorCodeConstants.FINANCE_ORDER_PRICE_ERROR);
                }
            } else {
                throw new ServiceException(ErrorCodeConstants.CONTRACT_RENT_ORDER_NOT_EXISTS);
            }
        }
        //清算单防重复提交
        antiDuplicateClearing(reqDTO);


        // 下单业务数据
        FinancePaymentDTO financePaymentDTO = getFinancePaymentDTO(reqDTO.getSourceBizId(), reqDTO.getSourceBizType());

        FinanceMoneyFlowDO.FinanceMoneyFlowDOBuilder builder = FinanceMoneyFlowDO.builder();
        PayOrderConvert.INSTANCE.convert(financePaymentDTO, builder);
        PayOrderConvert.INSTANCE.convert(reqDTO, builder);
        builder.tradeAmount(reqDTO.getPrice());
        builder.operator(SecurityFrameworkUtils.getLoginUser().getNickname());

        FinanceMoneyFlowDO financeMoneyFlowDO = builder.flowNo(financeMoneyFlowService.nextFlowNo())
                .sourceFrom(PaymentSourceEnum.OFFLINE.name())
                .status(FinanceCommonStatusEnum.WAITING.name()).build();
        financeMoneyFlowMapper.insert(financeMoneyFlowDO);
        financeMoneyFlowDO.setTenantId(SecurityFrameworkUtils.getLoginUser().getTenantId());
        //租金账单不去发送消费
        if (SourceBizTypeEnum.RENT_ORDER.name().equals(financeMoneyFlowDO.getSourceBizType())) {
            //修改账单状态为处理中
            CommonResult<Boolean> booleanCommonResult =
                    contractRentOrderApi.updateOrderStatus(rentOrder.getData().getId(),
                            OrderPayStatusEnum.PROCESS.getCode(), rentOrder.getData().getPayStatus());
            if (!booleanCommonResult.isSuccess()) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_TYPE_ERROR);
            }
            return financeMoneyFlowDO.getId();
        }

        //企业账单
        if (SourceBizTypeEnum.COMPANY_RENT_ORDER.name().equals(financeMoneyFlowDO.getSourceBizType())) {
            //批量修改账单状态为处理中
            CommonResult<Boolean> booleanCommonResult =
                    contractRentOrderApi.updateOrderBatchStatus(reqDTO.getSourceBizId(),
                            OrderPayStatusEnum.PROCESS.getCode());
            if (!booleanCommonResult.isSuccess()) {
                throw new ServiceException(ErrorCodeConstants.FINANCE_ACCOUNT_TYPE_ERROR);
            }
            return financeMoneyFlowDO.getId();
        }
        //退租流入不进行处理
        if (SourceBizTypeEnum.SETTLEMENT_BILL.name().equals(financeMoneyFlowDO.getSourceBizType())) {
            return financeMoneyFlowDO.getId();
        }
        payResultProducer.sendPaySuccessMessage(financeMoneyFlowDO);
        return financeMoneyFlowDO.getId();
    }

    /**
     * 防重复清算
     *
     * @param reqDTO
     */
    private void antiDuplicateClearing(PayOrderCreateReqDTO reqDTO) {
        //防止重复清算
        if (SourceBizTypeEnum.SETTLEMENT_BILL.name().equals(reqDTO.getSourceBizType())) {
            List<FinanceMoneyFlowDO> financeMoneyFlowDOS =
                    financeMoneyFlowMapper.selectList(new LambdaQueryWrapper<FinanceMoneyFlowDO>().eq(FinanceMoneyFlowDO::getSourceBizId, reqDTO.getSourceBizId())
                            .eq(FinanceMoneyFlowDO::getSourceBizType, reqDTO.getSourceBizType())
                            .in(FinanceMoneyFlowDO::getStatus, FinanceCommonStatusEnum.WAITING.name(),
                                    FinanceCommonStatusEnum.SUCCESS.name()));
            if (CollectionUtil.isNotEmpty(financeMoneyFlowDOS)) {
                long count =
                        financeMoneyFlowDOS.stream().filter(f -> FinanceCommonStatusEnum.WAITING.name().equals(f.getStatus())).count();
                if (count > 0) {
                    throw new ServiceException(ErrorCodeConstants.SETTLEMENT_BILL_WAITING);
                }
                long count1 =
                        financeMoneyFlowDOS.stream().filter(f -> FinanceCommonStatusEnum.SUCCESS.name().equals(f.getStatus())).count();
                if (count1 > 0) {
                    throw new ServiceException(ErrorCodeConstants.SETTLEMENT_BILL_SUCCESS);
                }
            }
        }
    }

    private PayOrderDO createPayOrder(AppPayOrderSubmitReqVO reqVO, FinancePaymentDTO reqDTO, PayChannelDO channel) {
        // 校验 App
        PayAppDO app = appService.validPayApp(reqVO.getAppId());
        // 查询对应的支付交易单是否已经存在。如果是，则直接返回
        // TODO lock
        PayOrderDO order = orderMapper.selectBySourceIdAndSourceType(
                reqDTO.getSourceBizId(), reqDTO.getSourceBizType());
        if (order != null) {
            log.warn("[createPayOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(),
                    order.getMerchantOrderId(), toJsonString(order)); // 理论来说，不会出现这个情况
            return order;
        }

        if(reqVO.getPrice() != null && reqVO.getPrice().doubleValue() > 0){
            reqDTO.setTradeAmount(reqVO.getPrice());
        }

        // 创建支付交易单
        return PayOrderDO.builder()
                .merchantId(app.getMerchantId())
                .appId(app.getId())
                .channelCode(reqVO.getChannelCode())
                .channelId(channel.getId())
                .subject(reqDTO.getSubject())
                .sourceBizId(reqVO.getSourceBizId())
                .sourceBizType(reqVO.getSourceBizType())
                .amount(reqDTO.getTradeAmount().multiply(BigDecimal.valueOf(100)).longValue())
                // 商户相关字段
                .notifyUrl(app.getPayNotifyUrl())
                .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus())
                // 订单相关字段
                .status(FinanceCommonStatusEnum.WAITING.name())
                // 退款相关字段
                .refundStatus(PayRefundTypeEnum.NO.getStatus())
                .refundTimes(0)
                .refundAmount(0L)
                .expireTime(LocalDateTime.now().plusMinutes(expireMinutes))
                .couponId(reqVO.getCouponIds())
                .discount(reqVO.getDiscount() == null ? 0 : reqVO.getDiscount())
                .channelFeeRate(channel.getFeeRate())
                .build();
    }

    @Override
    public AppPayOrderSubmitRespVO submitPayOrder(AppPayOrderSubmitReqVO reqVO, LoginUser user, String userIp) {
        if (PayChannelEnum.TONGSHANGYUN_ALIPAY_MINIAPP.getCode().equals(reqVO.getChannelCode())
                || PayChannelEnum.TONGSHANGYUN_WX_MINIAPP.getCode().equals(reqVO.getChannelCode())){
            return submitTongshangyunPayOrder(reqVO, user, userIp);
        }
        // 下单业务数据
        FinancePaymentDTO financePaymentDTO = getFinancePaymentDTO(reqVO.getSourceBizId(), reqVO.getSourceBizType());
        checkPayEnable(financePaymentDTO, user.getMobile());
        boolean couponLocked = checkCouponDiscount(financePaymentDTO, reqVO);
        try {
            // 校验支付渠道是否有效
            PayChannelDO channel = validatePayChannelCanSubmit(reqVO.getAppId(), user.getTenantId(), reqVO.getChannelCode());
            PayClient client = payClientFactory.getPayClient(channel.getId());
            Map<String, Object> payChannelParam = null;
            if (Objects.nonNull(financePaymentDTO.getApartmentId())){
                // 检查门店支付渠道配置
                payChannelParam = validateApartmentPayChannel(financePaymentDTO.getApartmentId(),reqVO.getChannelCode());
            }

            //todo 查询分账配置表,新增到我们的分账记录表

            // 创建订单
            PayOrderDO order = createPayOrder(reqVO, financePaymentDTO, channel);
            // 检查订单是否已支付
            checkPayResult(client, order);
            // 调用三方支付接口
            PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO)
                    .setSubject(order.getSubject()).setBody(order.getBody())
                    .setNotifyUrl(genChannelPayNotifyUrl(channel))
                    .setReturnUrl(genChannelReturnUrl(channel))
                    .setAmount(order.getAmount() - order.getDiscount())
                    .setExpireTime(order.getExpireTime())
                    .setPayChannelParam(payChannelParam);
//            // 测试环境默认1分钱 || 优惠后金额至少支付1分钱
//            if (unifiedOrderReqDTO.getAmount() <= 0 || !"prod".equals(profile)) {
//                unifiedOrderReqDTO.setAmount(1L);
//            }
            if (org.apache.commons.lang3.StringUtils.isBlank(unifiedOrderReqDTO.getMerchantOrderId())) {
                unifiedOrderReqDTO.setMerchantOrderId(order.getSourceBizType() + "_" + System.currentTimeMillis());
            }
            PayCommonResult<PayOrderUnifiedRespDTO<ObjectNode>> unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);
            unifiedOrderRespDTO.checkError();
            // 落库
            transactionTemplate.executeWithoutResult(status -> {
                if (order.getId() != null) {
                    // 更新支付订单信息
                    orderMapper.updateById(PayOrderDO.builder().id(order.getId())
                            .request(unifiedOrderRespDTO.getData().getRawRequest())
                            .response(JsonUtils.toJsonString(unifiedOrderRespDTO.getData().getRawResponse()))
                            .userIp(userIp)
                            .merchantOrderId(unifiedOrderRespDTO.getData().getMerchantOrderId())
                            .build());
                } else {
                    // 插入支付订单与交易流水
                    orderMapper.insert(order
                            .setRequest(unifiedOrderRespDTO.getData().getRawRequest())
                            .setResponse(JsonUtils.toJsonString(unifiedOrderRespDTO.getData().getRawResponse()))
                            .setUserIp(userIp)
                            .setMerchantOrderId(unifiedOrderRespDTO.getData().getMerchantOrderId()));

                    createFinanceMoneyFlow(order, channel, financePaymentDTO, unifiedOrderRespDTO.getData());
                }
            });
            if (!CollectionUtils.isEmpty(reqVO.getCouponIds())) {
                couponUnlockDelayProducer.send(order.getId());
            }
            return (AppPayOrderSubmitRespVO) new AppPayOrderSubmitRespVO()
                    .setOrderId(order.getId())
                    .setMerchantOrderId(unifiedOrderRespDTO.getData().getMerchantOrderId())
                    .setDisplayMode(unifiedOrderRespDTO.getData().getDisplayMode())
                    .setDisplayContent(unifiedOrderRespDTO.getData().getDisplayContent())
                    .setOtherContent(JsonUtils.toJsonString(unifiedOrderRespDTO.getData().getRawResponse()));
        } catch (Exception e) {
            if (couponLocked) {
                try {
                    CouponUnLockVo unlock = new CouponUnLockVo();
                    unlock.setIds(reqVO.getCouponIds());
                    couponApi.couponUntieLock(unlock);
                } catch (Exception ignore) {
                }
            }
            throw e;
        }
    }

    private Map<String, Object> validateApartmentPayChannel(Long apartmentId, String channelCode){
        String payChannelType = channleCodeToPayChannelType(channelCode);
        if (org.apache.commons.lang3.StringUtils.isBlank(payChannelType)){
            return null;
        }
        FinanceCollectionConfigRespVO financeCollectionConfigRespVO = financeCollectionConfigService.get(apartmentId, payChannelType);
        if (Objects.isNull(financeCollectionConfigRespVO)){
            throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAYMENT_PROVIDER_ERROR);
        }
        return BeanUtil.beanToMap(financeCollectionConfigRespVO);
    }

    private String channleCodeToPayChannelType(String channelCode){
        PayChannelEnum byCode = PayChannelEnum.getByCode(channelCode);
        String payChannelType = "";
        switch (byCode){
            case TONGSHANGYUN_WX_MINIAPP:
            case TONGSHANGYUN_ALIPAY_MINIAPP:
                payChannelType = PayChannelTypeEnum.TongShangYun.getCode();
                break;
            case TONGLIAN_MINIAPP_CASHIER:
            case TONGLIAN_ALIPAY_CASHIER:
                payChannelType = PayChannelTypeEnum.TongLian.getCode();
                break;
        }
        return payChannelType;
    }

    private void checkPayResult(PayClient client, PayOrderDO order) {
        if (StringUtils.hasText(order.getMerchantOrderId())) {
            PayCommonResult<PayQueryUnifiedRespDTO<ObjectNode>> result = null;
            try {
                result = client.unifiedQuery(PayQueryUnifiedReqDTO.builder()
                        .merchantOrderId(order.getMerchantOrderId())
                        .build());
            } catch (Exception ignore) {
            }
            if (result != null && result.getData() != null && result.getData().getStatus() == PayStatusEnum.SUCCESS) {
                throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAID);
            }
        }
    }

    private void checkPayEnable(FinancePaymentDTO financePaymentDTO, String phone) {
        if (payWhitelist.isEnabled()) {
            // 黑名单
            if (!CollectionUtils.isEmpty(payWhitelist.getBlacklist()) && payWhitelist.getBlacklist().contains(phone)) {
                throw new ServiceException(403, StringUtils.hasText(payWhitelist.getMessage()) ? payWhitelist.getMessage()
                        : "线上支付功能已禁用");
            }
            if (financePaymentDTO.getApartmentId() != null && !CollectionUtils.isEmpty(payWhitelist.getApartmentId())) {
                // 配置了门店, 检查是否在配置中
                if (payWhitelist.getApartmentId().contains(financePaymentDTO.getApartmentId())) {
                    return;
                // 不在配置中, 检查是否在手机白名单
                } else if (!CollectionUtils.isEmpty(payWhitelist.getWhitelist()) && payWhitelist.getWhitelist().contains(phone)) {
                    return;
                }
                throw new ServiceException(403, StringUtils.hasText(payWhitelist.getMessage()) ? payWhitelist.getMessage()
                        : "线上支付未启用");
            }
            // 业务数据没有门店, 判断是否在手机白名单
            if (!CollectionUtils.isEmpty(payWhitelist.getWhitelist()) && !payWhitelist.getWhitelist().contains(phone)) {
                throw new ServiceException(403, StringUtils.hasText(payWhitelist.getMessage()) ? payWhitelist.getMessage()
                        : "线上支付未启用");
            }
            return;
        }
        throw new ServiceException(403, StringUtils.hasText(payWhitelist.getMessage()) ? payWhitelist.getMessage()
                : "线上支付未启用");
    }

    private boolean checkCouponDiscount(FinancePaymentDTO financePaymentDTO, AppPayOrderSubmitReqVO reqVO) {
        if (!CollectionUtils.isEmpty(reqVO.getCouponIds())) {
            // 只能用于租金支付优惠
            if (SourceBizTypeEnum.RENT_ORDER.name().equals(reqVO.getSourceBizType()) && financePaymentDTO.getRental().compareTo(BigDecimal.ZERO) > 0) {
                log.info("锁定优惠券: {}", reqVO.getCouponId());
                CommonResult<List<CouponUpdateReturn>> lockResult;
                try {
                    CouponLockVo l = new CouponLockVo();
                    l.setIds(reqVO.getCouponIds());
                    l.setLockedDuration(expireMinutes);
                    l.setLockedTime(new Date());
                    l.setBillId(Long.valueOf(reqVO.getSourceBizId()));
                    l.setBillNo(financePaymentDTO.getRoomRentOrderNo());
                    l.setContractId(financePaymentDTO.getRoomContractId());
                    l.setStoreId(financePaymentDTO.getApartmentId());
                    l.setTotalAmount(financePaymentDTO.getRental());
                    l.setTenantUserId(financePaymentDTO.getMemberId());
                    lockResult = couponApi.couponAddLock(l);
                } catch (Exception e) {
                    throw new ServiceException(ErrorCodeConstants.PAY_COUPON_GET_LOCK_FAILED);
                }
                List<CouponUpdateReturn> cl = lockResult.getCheckedData();
                BigDecimal discount = BigDecimal.ZERO;
                int c = cl.size();
                int n = 0;
                for (CouponUpdateReturn couponUpdateReturn : cl) {
                    discount = discount.add(couponUpdateReturn.getRealityPreferentialAmount());
                    n++;
                    if (discount.compareTo(financePaymentDTO.getRental()) >= 0 && n != c) {
                        try {
                            CouponUnLockVo unlock = new CouponUnLockVo();
                            unlock.setIds(reqVO.getCouponIds());
                            couponApi.couponUntieLock(unlock);
                        } catch (Exception ignore) {
                        }
                        throw new ServiceException(ErrorCodeConstants.PAY_COUPON_ENOUGH);
                    }
                }
                // 优惠金额不能超过租金
                if (discount.compareTo(financePaymentDTO.getRental()) > 0) {
                    discount = financePaymentDTO.getRental();
                }
                reqVO.setDiscount(discount.multiply(BigDecimal.valueOf(100)).longValue());
                return true;
            }
            throw new ServiceException(ErrorCodeConstants.PAY_COUPON_RENTAL_ONLY);
        }
        return false;
    }

    private void createFinanceMoneyFlow(PayOrderDO order,
                                        PayChannelDO channel,
                                        FinancePaymentDTO financePaymentDTO,
                                        PayOrderUnifiedRespDTO<ObjectNode> unifiedOrder) {
        FinanceMoneyFlowDO financeMoneyFlowDO = PayOrderConvert.INSTANCE.convert(financePaymentDTO);
        financeMoneyFlowDO.setFlowNo(financeMoneyFlowService.nextFlowNo());
        financeMoneyFlowDO.setMerchantOrderId(unifiedOrder.getMerchantOrderId());
        financeMoneyFlowDO.setTradeNo(unifiedOrder.getTransactionId());
        financeMoneyFlowDO.setPayMethod(PayChannelEnum.getByCode(channel.getCode()).getShotName());
        financeMoneyFlowDO.setPayOrderId(order.getId());
        financeMoneyFlowDO.setPayAppId(channel.getAppId());
        financeMoneyFlowDO.setPayChannelId(channel.getId());
        financeMoneyFlowDO.setPayMerchantId(channel.getMerchantId());
        financeMoneyFlowDO.setStatus(FinanceCommonStatusEnum.WAITING.name());
        financeMoneyFlowDO.setSourceFrom(PaymentSourceEnum.ONLINE.name());
        financeMoneyFlowDO.setSourceBizId(order.getSourceBizId());
        financeMoneyFlowDO.setSourceBizType(order.getSourceBizType());
        financeMoneyFlowDO.setTenantId(financePaymentDTO.getTenantId());
        financeMoneyFlowDO.setDeptId(financeMoneyFlowDO.getDeptId());
        financeMoneyFlowDO.setDiscount(BigDecimal.valueOf(order.getDiscount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
        financeMoneyFlowDO.setOperator("租客");
        financeMoneyFlowMapper.insert(financeMoneyFlowDO);
    }

    private FinancePaymentDTO getFinancePaymentDTO(String sourceBizId, String sourceBizType) {
        // 个人余额充值
        if (SourceBizTypeEnum.ACCOUNT_RECHARGE.name().equals(sourceBizType)) {
            FinanceAccountJournalDO accountJournalDO = financeAccountJournalMapper.selectById(Long.valueOf(sourceBizId));
            if (accountJournalDO == null) {
                throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAYMENT_PROVIDER_ERROR);
            }
            FinanceAccountDO account = financeAccountMapper.selectById(accountJournalDO.getAccountId());
            if (account == null) {
                throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAYMENT_PROVIDER_ERROR);
            }
            // 根据用户memberid获取合同信息 获取到门店id
            CommonResult<List<ContractRoomReqVO>> allContractByRoomMateUserId;
            Long apartmentId = null;
            String apartmentName = null;
            try {
                Long memberId = Long.valueOf(account.getCustId());
                allContractByRoomMateUserId = contractApi.getAllContractByRoomMateUserId(memberId);
                List<ContractRoomReqVO> checkedData = allContractByRoomMateUserId.getCheckedData();
                if (!CollectionUtils.isEmpty(checkedData)){
                    ContractRoomReqVO contractRoomReqVO = checkedData.get(0);
                    apartmentId = contractRoomReqVO.getApartmentId();
                    apartmentName = contractRoomReqVO.getApartmentName();
                }
            } catch (NumberFormatException e) {
                throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAYMENT_PROVIDER_ERROR);
            }
            return FinancePaymentDTO.builder().subject("充值")
                    .expireInMinute(30)
                    .trader(SecurityFrameworkUtils.getLoginUser().getNickname())
                    .flowSubject(0)
                    .operator("租客")
                    .tenantId(SecurityFrameworkUtils.getLoginUser().getTenantId())
                    .direction(DirectionEnum.IN.getValue())
                    .tradeAmount(accountJournalDO.getAmount())
                    .tradeTime(accountJournalDO.getTradeTime())
                    .memberId(SecurityFrameworkUtils.getLoginUserId())
                    .apartmentId(apartmentId)
                    .apartmentName(apartmentName)
                    .build();
        }
        FinancePaymentApi financePaymentApi = financePaymentApiObjectProvider.get(sourceBizType);
        if (financePaymentApi == null) {
            throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAYMENT_PROVIDER_ERROR);
        }
        CommonResult<FinancePaymentDTO> result;
        try {
            result = financePaymentApi.createPayment(sourceBizId, sourceBizType);
        } catch (Exception e) {
            throw new ServiceException(ErrorCodeConstants.PAY_ORDER_PAYMENT_PROVIDER_ERROR);
        }

        // 账单支付使用收款配置

        return result.getCheckedData();
    }

    private PayOrderDO validatePayOrderCanSubmit(Long id) {
        PayOrderDO order = orderMapper.selectById(id);
        if (order == null) { // 是否存在
            throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
        }
        if (!FinanceCommonStatusEnum.WAITING.name().equals(order.getStatus())) { // 校验状态，必须是待支付
            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
        }
        return order;
    }

    private PayChannelDO validatePayChannelCanSubmit(Long appId, Long tenantId, String channelCode) {
        // 校验 App
        appService.validPayApp(appId);
        // 校验支付渠道是否有效
        PayChannelDO channel = channelService.validPayChannel(appId, channelCode);
        if (!channel.getMerchantId().equals(tenantId)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_NOT_FOUND);
        }
        // 校验支付客户端是否正确初始化
        PayClient client = payClientFactory.getPayClient(channel.getId());
        if (client == null) {
            log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
            throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
        }
        return channel;
    }

    /**
     * 根据支付渠道的编码，生成支付渠道的返回地址
     *
     * @param channel 支付渠道
     * @return 支付成功返回的地址。 配置地址 + "/" + channel id
     */
    private String genChannelReturnUrl(PayChannelDO channel) {
        return payProperties.getPayReturnUrl() + "/" + channel.getId();
    }

    /**
     * 根据支付渠道的编码，生成支付渠道的回调地址
     *
     * @param channel 支付渠道
     * @return 支付渠道的回调地址  配置地址 + "/" + channel id
     */
    private String genChannelPayNotifyUrl(PayChannelDO channel) {
        return payProperties.getPayNotifyUrl() + "/" + channel.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyDataDTO rawNotify) {
        // 校验支付渠道是否有效
        PayChannelDO channel = channelService.validPayChannel(channelId);
        TenantUtils.execute(channel.getMerchantId(), () -> {
            // 1. 更新 PayOrderExtensionDO 支付成功
//            PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getMerchantOrderId(),
//                    rawNotify);
            // 2. 更新 PayOrderDO 支付成功
            PayOrderDO order;
            try {
                order = updatePayOrderSuccess(channel, notify);
            } catch (ServiceException ignore) {
                return;
            }
            // 3. 插入支付通知记录
//            notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
//                    .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build());
            log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId());
            int c = financeMoneyFlowMapper.updateStatusByPayOrderIdAndStatus(order.getId(), FinanceCommonStatusEnum.WAITING.name(), FinanceMoneyFlowDO.builder()
                    .payTime(notify.getSuccessTime())
                    .status(FinanceCommonStatusEnum.SUCCESS.name())
                    .build());
            if (c > 0) {
                FinanceMoneyFlowDO financeMoneyFlowDO = financeMoneyFlowMapper.selectOne("merchant_order_id", notify.getMerchantOrderId());
                payResultProducer.sendPaySuccessMessage(order, financeMoneyFlowDO, notify);
                notifyCouponUse(order);
            }
        });
    }

    private void updateDivideDetailSuccess(PayOrderNotifyRespDTO notify) {
        //  判断 FinanceDivideDetailDO 是否处于待支付
        FinanceDivideDetailDO detailDO = financeDivideDetailMapper.selectByThirdNo(notify.getMerchantOrderId());
//        if (detailDO == null) {
//            throw exception(ErrorCodeConstants.DIVIDE_DETAIL_NOT_FOUND);
//        }
        if (!FinanceCommonStatusEnum.WAITING.name().equals(detailDO.getStatus())) { // 校验状态，必须是待支付
            throw exception(ErrorCodeConstants.DIVIDE_DETAIL_STATUS_IS_NOT_WAITING);
        }
        int count = financeDivideDetailMapper.updateStatusByOrderIdAndStatus(detailDO.getSourceBizId(), FinanceCommonStatusEnum.WAITING.name(), FinanceDivideDetailDO.builder()
                .status(FinanceCommonStatusEnum.SUCCESS.name())
                .build());

//        if (count == 0) { // 校验状态，必须是待支付
//            throw exception(ErrorCodeConstants.DIVIDE_DETAIL_STATUS_IS_NOT_WAITING);
//        }
    }

    private void notifyCouponUse(PayOrderDO order) {
        if (CollectionUtils.isEmpty(order.getCouponId())) {
            return;
        }
        log.info("确认优惠券使用 {}", order.getCouponId());
        CommonResult<Boolean> result = couponApi.updateCoupon(order.getCouponId().stream().map(id -> {
            CouponUpdateVo v = new CouponUpdateVo();
            v.setId(id);
            v.setUsageTime(new Date());
            return v;
        }).collect(Collectors.toList()));
        log.info("确认优惠券使用结果: {}", JsonUtils.toJsonString(result));
    }

    @Override
    public PageResult<PayOrderDO> selectExpiredPayOrder(PageParam page) {
        return TenantUtils.executeIgnoreAndReturn(() -> orderMapper.selectPage(page, new QueryWrapperX<PayOrderDO>()
                .le("expire_time", LocalDateTime.now())));
    }

    @Override
    public AppPayOrderSubmitRespVO submitTongshangyunPayOrder(AppPayOrderSubmitReqVO reqVO, LoginUser user, String userIp) {
        // 下单业务数据
        FinancePaymentDTO financePaymentDTO = getFinancePaymentDTO(reqVO.getSourceBizId(), reqVO.getSourceBizType());
        checkPayEnable(financePaymentDTO, user.getMobile());
        if(reqVO.getPrice() != null && reqVO.getPrice().doubleValue() > 0){
            financePaymentDTO.setTradeAmount(reqVO.getPrice());
        }
        boolean couponLocked = checkCouponDiscount(financePaymentDTO, reqVO);
        try {
            // 校验支付渠道是否有效
            PayChannelDO channel = validatePayChannelCanSubmit(reqVO.getAppId(), user.getTenantId(), reqVO.getChannelCode());
            PayClient client = payClientFactory.getPayClient(channel.getId());
            //校验用户是否有通商云账号
            String openId = reqVO.getChannelExtras().get("openId");
            //绑定个人用户
            String thirdUserId = bindThirdAccount(client, user, openId);
            // 创建订单
            PayOrderDO order = createPayOrder(reqVO, financePaymentDTO, channel);
            //todo 查询分账配置表,新增到我们的分账记录表
            FinanceCollectionConfigRespVO financeConfig = checkCollection(financePaymentDTO.getApartmentId());
            FinanceDivideRuleRespVO divideResult = packageDivideRule(order, financePaymentDTO.getApartmentId(), financeConfig);
             //主账号的accountId
            // 检查订单是否已支付
            checkPayResult(client, order);
            // 调用三方支付接口
            PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO)
                    .setSubject(order.getSubject()).setBody(order.getBody())
                    .setNotifyUrl(genChannelPayNotifyUrl(channel))
                    .setReturnUrl(genChannelReturnUrl(channel))
                    .setAmount(order.getAmount() - order.getDiscount())
                    .setExpireTime(order.getExpireTime())
                    .setDivideFlag(divideResult.getDivideFlag())
                    .setPayerId(user.getId().toString())
                    //收款方(主体账号)
                    .setRecieverId(financeConfig.getCollectionId().toString())
                    .setDivideRule(divideResult.getRule())
                    .setMerchantOrderId(order.getSourceBizId());
//            // 测试环境默认1分钱 || 优惠后金额至少支付1分钱
//            if (unifiedOrderReqDTO.getAmount() <= 0 || !"prod".equals(profile)) {
//                unifiedOrderReqDTO.setAmount(1L);
//            }
            PayCommonResult<PayOrderUnifiedRespDTO<ObjectNode>> unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);
            unifiedOrderRespDTO.checkError();
            // 落库
            transactionTemplate.executeWithoutResult(status -> {
                if (order.getId() != null) {
                    // 更新支付订单信息
                    orderMapper.updateById(PayOrderDO.builder().id(order.getId())
                            .request(unifiedOrderRespDTO.getData().getRawRequest())
                            .response(JsonUtils.toJsonString(unifiedOrderRespDTO.getData().getRawResponse()))
                            .userIp(userIp)
                            .merchantOrderId(unifiedOrderRespDTO.getData().getMerchantOrderId())
                            .build());
                } else {
                    // 插入支付订单与交易流水
                    orderMapper.insert(order
                            .setRequest(unifiedOrderRespDTO.getData().getRawRequest())
                            .setResponse(JsonUtils.toJsonString(unifiedOrderRespDTO.getData().getRawResponse()))
                            .setUserIp(userIp)
                            .setMerchantOrderId(unifiedOrderRespDTO.getData().getMerchantOrderId()));

                    createFinanceMoneyFlow(order, channel, financePaymentDTO, unifiedOrderRespDTO.getData());
                    //插入分账明细
                    if (divideResult.getDivideFlag()) {
                        List<FinanceDivideDetailDO> divideResultDtos = divideResult.getDtos();
                        divideResultDtos.stream().peek(d -> d.setTradeNoThird(unifiedOrderRespDTO.getData().getMerchantOrderId()));
                        financeDivideDetailMapper.insertBatch(divideResultDtos);
                    }

                }
            });
            if (!CollectionUtils.isEmpty(reqVO.getCouponIds())) {
                couponUnlockDelayProducer.send(order.getId());
            }
            //给前端返回收银宝信息
             HashMap hashMap = JsonUtils.parseObject(unifiedOrderRespDTO.getData().getRawResponse().toString(), HashMap.class);
            return (AppPayOrderSubmitRespVO) new AppPayOrderSubmitRespVO()
                    .setOrderId(order.getId())
                    .setMerchantOrderId(unifiedOrderRespDTO.getData().getMerchantOrderId())
                    .setDisplayMode(unifiedOrderRespDTO.getData().getDisplayMode())
                    .setDisplayContent(unifiedOrderRespDTO.getData().getDisplayContent())
                    .setOtherContent(JsonUtils.toJsonString( hashMap.get("miniprogramPayInfo_VSP")));
        } catch (Exception e) {
            if (couponLocked) {
                try {
                    CouponUnLockVo unlock = new CouponUnLockVo();
                    unlock.setIds(reqVO.getCouponIds());
                    couponApi.couponUntieLock(unlock);
                } catch (Exception ignore) {
                }
            }
            throw e;
        }
    }

    @Override
    public void notifyTongshangyunPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyDataDTO rawNotify) {

    }

    private String bindThirdAccount(PayClient client, LoginUser user, String openId) {
        String userId = "";
        try {
            CommonResult<MemberUserRespDTO> member = memberUserApi.getUserById(user.getId());
            if (member.isSuccess() && !ObjectUtils.isEmpty(member.getData())) {
                MemberUserRespDTO data = member.getData();
                Long bizUserId = data.getId();
                if (!StringUtils.hasText(data.getThirdMemberId())) {
                    //去创建会员
                    MemberCreateReqDTO createReqDTO = new MemberCreateReqDTO().setBizUserId(bizUserId.toString())
                            .setMemberType(3L).setSource(1L);
                    try {
                        log.info("请求通商云创建个人用户，请求参数：{}", JSONObject.toJSONString(createReqDTO));
                        PayCommonResult<MemberCreateRespDTO> createMember = client.createMember(createReqDTO);
                        log.info("请求通商云创建个人用户，返回参数：{}", JSONObject.toJSONString(createMember));
                        if (createMember.isSuccess()) {
                            MemberCreateRespDTO createMemberData = createMember.getData();
                            String thirdNO = createMemberData.getUserId();
                            data.setThirdMemberId(thirdNO);
                            ThirdPayUserReqDTO thirdDTO = new ThirdPayUserReqDTO();
                            thirdDTO.setThirdMemberId(thirdNO);
                            thirdDTO.setMemberId(bizUserId);
                            memberUserApi.registerThirdMember(thirdDTO);
                            userId = thirdNO;
                            //并且绑定
                            bindOpenId(client, openId, bizUserId);

                        }
                    } catch (Exception e) {
                        log.error("通商云创建用户出错{}", e.getMessage());
                        throw exception(CREATE_MEMBER_ERROR);
                    }
                } else {
                    //是否已绑定标识
                    if (!data.getBindFlag()) {
                        bindOpenId(client, openId, bizUserId);
                    }
                    userId = data.getThirdMemberId();
                }
            } else {
                log.error("获取会员信息失败");
                throw exception(GET_MEMBER_ERROR);
            }
        } catch (Exception e) {
            log.error("获取会员信息出错{}", e.getMessage());
            throw exception(GET_MEMBER_ERROR);
        }

        return userId;
    }

    public void bindOpenId(PayClient client, String openId, Long bizUserId) {
        //绑定标识//todo
        MemberBindReqDTO bindReqDTO = new MemberBindReqDTO().setBizUserId(bizUserId.toString())
                .setOperationType("set").setAcctType("weChatMiniProgram").setAcct(openId);
        log.info("请求通商云绑定支付标识，请求参数：{}", JSONObject.toJSONString(bindReqDTO));
        try {
            PayCommonResult<MemberBindRespDTO> bindResult = client.memberBind(bindReqDTO);
            log.info("请求通商云绑定支付标识，返回参数：{}", JSONObject.toJSONString(bindResult));
            if (bindResult.isSuccess()) {
                memberUserApi.bindPayFlag(bizUserId);
            }
        } catch (Exception e) {
            log.error("通商云绑定支付标识出错{}", e.getMessage());
            throw exception(BIND_MEMBER_ERROR);
        }
    }


    private FinanceCollectionConfigRespVO checkCollection(Long apartmentId) {
        //查询门店的收款主体
        FinanceCollectionConfigRespVO financeConfig = financeCollectionConfigService.get(apartmentId, PayChannelTypeEnum.TongShangYun.getCode());
        if (ObjectUtils.isEmpty(financeConfig)) {
            log.error("[packageDivideInfo][门店({}) 找不到对应的收款主体]", apartmentId);
            throw exception(ErrorCodeConstants.FINANCE_COLLECTION_ERROR);
        }
        return financeConfig;
    }

    private FinanceDivideRuleRespVO packageDivideRule(PayOrderDO order, Long apartmentId, FinanceCollectionConfigRespVO config) {
        FinanceDivideRuleRespVO result = new FinanceDivideRuleRespVO();
        List<FinanceDivideDetailDO> list = new ArrayList<>();
        List<DivideRule> ruleList = new ArrayList<>();
        //组装分账规则，需要在传参里多加apartmentId
        List<FinanceDivideAccountDo> financeDivideAccount = financeDivideService.getFinanceDivideAccount(apartmentId);
        if (!CollectionUtils.isEmpty(financeDivideAccount)) {
            FinanceDivideAccountDo financeDivideAccountDo = financeDivideAccount.get(0);
            //只有分账开启时才会分账有分账规则
            if (financeDivideAccountDo.getStatus().intValue() == 1) {
                //判断分账配置表里是否配置收款主体
                boolean flag = financeDivideAccount.stream().anyMatch(f -> (f.getApartmentId().intValue() == config.getApartmentId().intValue())
                        && (f.getAccountNo().equals(config.getCollectionNo()))
                        && (f.getAccountName().equals(config.getCollectionName()))
                );
                if (financeDivideAccount.size() < 2) {
                    log.error("[packageDivideInfo][门店({}) 分账配置配置商户少于2个]", apartmentId);
                    throw exception(ErrorCodeConstants.FINANCE_DIVIDE_ACCOUNT_SUM_ERROR);
                }
                if (!flag) {
                    log.error("[packageDivideInfo][门店({}) 分账配置未配置主体账号分账账号]", apartmentId);
                    throw exception(ErrorCodeConstants.FINANCE_PRIMARY_DIVIDE_ERROR);
                }
                //组装分账记录表记录
                Long total = order.getAmount() - order.getDiscount();
                for (FinanceDivideAccountDo divideAccount : financeDivideAccount) {
                    FinanceDivideDetailDO detailDO = new FinanceDivideDetailDO();
                    DivideRule divideRule = new DivideRule();
                    detailDO.setPrimaryFlag(divideAccount.getBigAccount());
                    detailDO.setSourceBizId(order.getSourceBizId());
                    detailDO.setSourceBizType(order.getSourceBizType());
                    if (divideAccount.getMode().intValue() == DivideModeEnum.RADIO.getCode().intValue()) {
                        //按比例
                        Long amount = (total * divideAccount.getRatio().intValue()) / 100;
                        detailDO.setAmount(amount);
                        detailDO.setRemark("按比例分账");
                    } else {
                        //分账账户收的费项类型支持多选
                        String[] feeItems = divideAccount.getFeeTypeItem().split(",");
                        if (Arrays.isNullOrContainsNull(feeItems)) {
                            log.error("[packageDivideInfo][账单id({}),商户名称{} ,费项为空]", order.getSourceBizId(), divideAccount.getAccountName(), divideAccount.getFeeTypeMode(),
                                    divideAccount.getFeeTypeItem());
                            throw exception(ErrorCodeConstants.FINANCE_FEE_EMPTY);
                        }

                        Long totalAmount = 0L;
                        for (String feeItem : feeItems) {
                            //按费用类型
                            OrderFeeAmountReqVO req = new OrderFeeAmountReqVO();
                            req.setFeeId(Long.parseLong(feeItem));
                            req.setSourceBizId(Long.parseLong(order.getSourceBizId()));
                            req.setFeeSort(divideAccount.getFeeTypeMode().toString());
                            try {//todo
                                CommonResult<OrderFeeAmountRespVO> orderAmountByFee = contractRentOrderApi.getOrderAmountByFee(req);
                                if (orderAmountByFee.isSuccess()) {
                                    OrderFeeAmountRespVO data = orderAmountByFee.getData();
                                    if (ObjectUtils.isEmpty(data)) {
                                        totalAmount += 0L;
                                    } else {
                                        //如果是房租费用，要去除优惠券金额
                                        if (data.getFeeName().equals("租金") && SourceBizTypeEnum.RENT_ORDER.name().equals(order.getSourceBizType())) {

                                            totalAmount += (data.getAmount().multiply(BigDecimal.valueOf(100)).longValue() - order.getDiscount());

                                        } else {
                                            totalAmount += data.getAmount().multiply(BigDecimal.valueOf(100)).longValue();
                                        }
                                    }

                                }
                            } catch (Exception e) {
                                log.error("[packageDivideInfo][账单id({}) ,费用类型为{},费项为{}获取支付金额失败]", order.getSourceBizId(), divideAccount.getFeeTypeMode(),
                                        divideAccount.getFeeTypeItem());
                                throw exception(ErrorCodeConstants.FINANCE_AMOUNT_BY_FEE_ERROR);
                            }
                        }
                        detailDO.setRemark("按费项分账");
                        detailDO.setAmount(totalAmount);
                    }
                    //过滤掉有0的数据
                    if (detailDO.getAmount().intValue() > 0) {
                        //手续费
                        long fee = detailDO.getAmount() * order.getChannelFeeRate().longValue();
                        detailDO.setFee(fee).setSourceBizId(order.getSourceBizId()).setSourceBizType(order.getSourceBizType())
                                .setApartmentId(apartmentId).setAccountNo(divideAccount.getAccountNo()).setAccountName(divideAccount.getAccountName())
                                .setAccountId(divideAccount.getAccountId())
                                .setStatus(FinanceCommonStatusEnum.WAITING.name());
                        divideRule.setBizUserId(divideAccount.getAccountId().toString()).setAmount(detailDO.getAmount()).setFee(detailDO.getFee()).setRemark(detailDO.getRemark());
                        list.add(detailDO);
                        ruleList.add(divideRule);
                    }
                }

                //如果是费项分账,则需要比对，这里不需要，只要算好分账账号的金额就可以了
                if (financeDivideAccountDo.getMode().intValue() == DivideModeEnum.EXPENSE_TYPE.getCode().intValue()) {
                    long sum = list.stream().mapToLong(FinanceDivideDetailDO::getAmount).sum();
                    if (sum < order.getAmount()) {
                        long balance = order.getAmount() - sum;
                        //可能分账配置缺少配置了，分到主账号
                        list.stream().filter(r -> r.getPrimaryFlag()).findFirst()
                                .ifPresent(r -> {
                                    r.setAmount(r.getAmount() + balance);
                                    r.setFee(r.getAmount() * order.getChannelFeeRate().longValue());
                                });
                        ruleList.stream().filter(r -> r.getBizUserId().equals(config.getCollectionNo()))
                                .findFirst().ifPresent(r -> {
                                    r.setAmount(r.getAmount() + balance);
                                    r.setFee(r.getAmount() * order.getChannelFeeRate().longValue());
                                });
                    }
                }

                DivideRule divideRule = ruleList.stream().filter(r -> r.getBizUserId().equals(config.getCollectionId().toString())).findFirst().orElse(null);
                List<DivideRule> rules = ruleList.stream().filter(r -> r != divideRule).collect(Collectors.toList());
                //如果rules为空，那么就改为不开启分账
                if (CollectionUtils.isEmpty(rules)) {
                    result.setDivideFlag(false);
                    divideRule.setAmount(order.getAmount() - order.getDiscount());
                    divideRule.setFee(divideRule.getAmount() * order.getChannelFeeRate().longValue());
                    divideRule.setRemark("不分账");
                } else {
                    divideRule.setRuleList(rules);
                    result.setDtos(list);
                    result.setRule(divideRule);
                    result.setDivideFlag(true);
                }
            } else {
                //没有开启分账设置
                result.setDivideFlag(false);
                DivideRule divideRule = new DivideRule();
                divideRule.setAmount(order.getAmount() - order.getDiscount());
                divideRule.setFee(divideRule.getAmount() * order.getChannelFeeRate().longValue());
                divideRule.setRemark("不分账");
            }
        }
        return result;
    }

    /**
     * 更新 PayOrderExtensionDO 支付成功
     *
     * @param no        支付订单号（支付模块）
     * @param rawNotify 通知数据
     * @return PayOrderExtensionDO 对象
     */
    private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyDataDTO rawNotify) {
        // 1.1 查询 PayOrderExtensionDO
        PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no);
        if (orderExtension == null) {
            throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
        }
        if (ObjectUtil.notEqual(orderExtension.getStatus(), FinanceCommonStatusEnum.WAITING.name())) { // 校验状态，必须是待支付
            throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
        }
        // 1.2 更新 PayOrderExtensionDO
        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),
                FinanceCommonStatusEnum.WAITING.name(), PayOrderExtensionDO.builder().id(orderExtension.getId())
                        .status(FinanceCommonStatusEnum.SUCCESS.name())
                        .channelNotifyData(toJsonString(rawNotify)).build());
        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
        }
        log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
        return orderExtension;
    }

    /**
     * 更新 PayOrderDO 支付成功
     *
     * @param channel 支付渠道
     * @param notify  通知回调
     * @return PayOrderDO 对象
     */
    private PayOrderDO updatePayOrderSuccess(PayChannelDO channel, PayOrderNotifyRespDTO notify) {
        // 2.1 判断 PayOrderDO 是否处于待支付
        PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId(channel.getAppId(), notify.getMerchantOrderId());
        if (order == null) {
            throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
        }
        if (!FinanceCommonStatusEnum.WAITING.name().equals(order.getStatus())) { // 校验状态，必须是待支付
            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
        }
        // 2.2 更新 PayOrderDO
        int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), FinanceCommonStatusEnum.WAITING.name(),
                PayOrderDO.builder().status(FinanceCommonStatusEnum.SUCCESS.name())
                        .channelId(channel.getId()).channelCode(channel.getCode())
                        .successTime(notify.getSuccessTime())
                        .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId())
                        .notifyTime(LocalDateTime.now()).build());
        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
        }
        return order;
    }


}
